#!/usr/bin/env python
#-*- coding: utf-8 -*-
#
# Copyright 2013 Antoine Drouin (poinix@gmail.com)
#
# This file is part of PAT.
#
#    PAT is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    PAT is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with PAT.  If not, see <http://www.gnu.org/licenses/>.
#

"""
  This is a parser for openvsp (http://openvsp.org/) files
  The aim is to produce avl files (http://web.mit.edu/drela/Public/web/avl/)
  for computing aero coefficients
"""

import logging
LOG = logging.getLogger(__name__)
#LOG.setLevel(logging.DEBUG)
LOG.setLevel(logging.INFO)
#LOG.setLevel(logging.ERROR)

import os, sys, re
import StringIO
import xml.etree.ElementTree as ET
import time, datetime
import math, numpy as np

import pat.utils as pu

class MassProp:
    def __init__(self, filename):
        self.filename = filename
        LOG.info("loading mass from {:s}".format(filename))
        parser = [(r'(\S+)\s+Total Mass',('mass',)),
                  (r'(\S+)\s+(\S+)\s+(\S+)\s+Center of Gravity',('cg_x', 'cg_y', 'cg_z')),
                  (r'(\S+)\s+(\S+)\s+(\S+)\s+Ixx, Iyy, Izz',('i_xx', 'i_yy', 'i_zz')),
                  (r'(\S+)\s+(\S+)\s+(\S+)\s+Ixy, Ixz, Iyz',('i_xy', 'i_xz', 'i_yz'))]
        f = open(filename)
        i = 0; self.values = {}
        for line in f:
            LOG.debug(" %s", line.replace('\n', ''))
            if i < len(parser):
                m=re.match(parser[i][0], line)
                if m:
                    LOG.debug(" match found %s %s", parser[i][1], m.groups())
                    for j, v in enumerate(m.groups()):
                        self.values[parser[i][1][j]] = v
                    i += 1
        LOG.debug(" matched %d/%d regular expressions in %s", i, len(parser), filename)
        f.close()

    def print_avl(self, f):
        f.write('''#
# Generated from {}
# on {}\n'''.format(self.filename, datetime.datetime.now()))
        f.write('''
Lunit = 1.0 m
Munit = 1.0 kg
Tunit = 1.0 s
g   = 9.81
rho = 1.225
#
#  mass   x     y     z       Ixx     Iyy    Izz    [ Ixy  Ixz  Iyz ]
+   0.   0.     0.    0.
''')
        fmt = '  {} {} {} {} {} {} {} {} {} {}'
        args = [self.values['mass'], self.values['cg_x'], self.values['cg_y'], self.values['cg_z'],
                self.values['i_xx'], self.values['i_yy'], self.values['i_zz'],
                self.values['i_xy'], self.values['i_xz'], self.values['i_yz']]
        f.write(fmt.format(*args))
        

class Component:
    def __init__(self, xml):
        self.type =  xml.find("Type").text
        LOG.debug("loading component: type {:s}".format(self.type))
        p = xml.find("General_Parms")
        if p <> None: 
            self.name = p.find("Name").text
            self.tran_x, self.tran_y, self.tran_z = [float(p.find(txt).text) for txt in ['Tran_X', 'Tran_Y', 'Tran_Z']]
            self.rot_x, self.rot_y, self.rot_z = [float(p.find(txt).text) for txt in ['Rot_X', 'Rot_Y', 'Rot_Z']]

    def print_avl(self, f):
        pass

class Section():
    def __init__(self, xml):
        self.ar =  float(xml.find("AR").text)       # aspect ratio
        self.tr =  float(xml.find("TR").text)       # taper ratio
        self.area = float(xml.find("Area").text)    #
        self.span = float(xml.find("Span").text)    #
        self.tc =  float(xml.find("TC").text)       #
        self.rc =  float(xml.find("RC").text)       #
        self.sweep =  float(xml.find("Sweep").text) #
        

class Wing(Component):
    def __init__(self, xml):
        Component.__init__(self, xml)
        LOG.info("loading wing ({:s})".format(self.name))
        p = xml.find("Mswing_Parms")
        self.total_area = float(p.find("Total_Area").text)
        self.total_span = float(p.find("Total_Span").text)
        self.sections = [Section(x) for x in xml.find("Section_List")]

    def print_avl(self, f):
        f.write('''
#==============================================================
#==============================================================
SURFACE
{:s}
5  1.0  12 -1.0   ! Nchord  Cspace   Nspan  Sspace
ANGLE
0.0     twist angle bias for whole surface
YDUPLICATE
0.0
SCALE
1.0 1.0 1.0  !Original values
TRANSLATE
{:.3f} {:.3f} {:.3f}
#  Xle    Yle       Zle        chord    angle   Nspan  Sspace
'''.format(self.name, self.tran_x, self.tran_y, self.tran_z))
        fmt='''
#--------------------------------------------- {}
SECTION 
{:.3f} {:.3f} {:.3f} {:.3f} {:.3f}
AFIL 0.0 1.0
ht12.dat
'''
        rmat = np.eye(3)
        if self.rot_x == 90.: rmat = np.array([[1., 0., 0.], [0., 0., 1.], [0., 1., 0.]])
        pos_le = np.array([0., 0., 0.])
        args = list(np.dot(rmat, pos_le))+[self.sections[0].rc, 0.000000]
        f.write(fmt.format(0, *args))
        for i, s in enumerate(self.sections):
            pos_le[0] += s.span*math.sin(pu.rad_of_deg(s.sweep))
            pos_le[1] += s.span
            args = list(np.dot(rmat, pos_le))+[s.tc, 0.000000]
            f.write(fmt.format(i+1, *args))
                
 

class Model:

    def __init__(self, filename, mass_filename=None):
        self.parse(filename)
        if mass_filename <> None: 
            self.massprop = MassProp(mass_filename)
            #print self.massprop.values

    def parse(self, filename):
        self.filename = filename
        LOG.info("loading model from: {:s}".format(self.filename))
        tree = ET.parse(filename)
        root = tree.getroot()
        self.name = root.find("Name").text
        self.version = root.find("Version").text
        self.cg_x = float(root.find("CG_X").text)       #
        self.cg_y = float(root.find("CG_Y").text)       #
        self.cg_z = float(root.find("CG_Z").text)       #

        self.components = []
        components = root.find("Component_List")
        for c in components.findall("Component"):
            t = c.find("Type")
            if t.text == "Mswing":
                self.components.append(Wing(c))
            else:
                self.components.append(Component(c))


    def print_avl(self, filename):
        LOG.info("writing avl to: {:s}.avl".format(filename))
        f = open(filename+'.avl', 'w')
        f.write('''#
# Generated from {}
# on {}\n'''.format(self.filename, datetime.datetime.now()))
        args = (self.name, self.cg_x, self.cg_y, self.cg_z)
        f.write('''#
{}                        ! Case Title 
#
0.0                       | Mach
0     0     0.0           | iYsym  iZsym  Zsym
#
0.55    0.185  2.6        | Sref   Cref   Bref    reference area, chord, span
{} {} {}                  | Xref   Yref   Zref    moment reference location
0.015                     | CDo
            '''.format(*args))

        for c in self.components:
            if c.type == 'Mswing':
                c.print_avl(f)
        f.close()
        try:
            f = open(filename+'.mass', 'w')
            self.massprop.print_avl(f)
            f.close()
            LOG.info("writing mass to: {:s}".format(filename))
        except Exception as e:
            print str(e)


if __name__ == "__main__":
    logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
    m = Model("../config/openvsp/AirplaneTemplate.vsp", mass_filename="../config/openvsp/AirplaneTemplate_massprop.txt")
    m.print_avl("../config/avl/airplane_template_ovsp")
